<?php

//ini_set("max_execution_time", "0");
require_once 'Taoapi_Config.php';
require_once 'Taoapi_Cache.php';
require_once 'Taoapi_Exception.php';
require_once 'Taoapi_Util.php';
require_once 'Taoapi_Session.php';

/**
 * 淘宝API处理类
 *
 * @category Taoapi
 * @package Taoapi
 * @copyright Copyright (c) 2008-2009 Taoapi (http://www.Taoapi.com)
 * @license    http://www.Taoapi.com
 * @version    Id: Taoapi  2009-12-22  12:30:51 旺旺:浪子Arvin QQ:8769852
 */
class Taoapi {

    protected $taobaoData;
    private $_userParam = array();
    private $_errorInfo;
    /**
     * @var Taoapi_Util
     */
    public static $Taoapi_Util;
    /**
     * @var Taoapi_Config
     */
    public $ApiConfig;
    /**
     * @var Taoapi_Cache
     */
    public $Cache;
    private $_ArrayModeData;

    public function __construct() {
        if (self::$Taoapi_Util == NULL) {

            self::$Taoapi_Util = new Taoapi_Util();
        }
        $Config = Taoapi_Config::Init();
        $this->ApiConfig = $Config->getConfig();
        $this->Cache = new Taoapi_Cache();
        $this->Cache->setClearCache($this->ApiConfig->ClearCache);
    }

    public function __set($name, $value) {
        if ($this->taobaoData && $this->ApiConfig->AutoRestParam) {

            $this->_userParam = array();
            $this->taobaoData = null;
        }

        $this->_userParam[$name] = $value;
    }

    public function setUserParam($userParam) {
        $this->_userParam = $userParam;
    }

    public function __get($name) {
        if (!empty($this->_userParam[$name])) {

            return $this->_userParam[$name];
        }
    }

    public function __unset($name) {
        unset($this->_userParam[$name]);
    }

    public function __isset($name) {
        return isset($this->_userParam[$name]);
    }

    public function __destruct() {
        $this->_userParam = array();
    }

    public function __toString() {
        return $this->createStrParam($this->_userParam);
    }

    /**
     * @return Taoapi
     */
    public function setRestNumberic($rest) {
        $this->ApiConfig->RestNumberic = intval($rest);

        return $this;
    }

    /**
     * @return Taoapi
     */
    public function setVersion($version, $signmode = 'md5') {
        $this->ApiConfig->Version = intval($version);

        $this->ApiConfig->SignMode = $signmode;

        return $this;
    }

    /**
     * @return Taoapi
     */
    public function setCloseError() {
        $this->ApiConfig->CloseError = false;

        return $this;
    }

    private function FormatUserParam($param) {
        if (strtoupper($this->ApiConfig->Charset) != 'UTF-8') {
            if (function_exists('mb_convert_encoding')) {
                if (is_array($param)) {
                    foreach ($param as $key => $value) {
                        $param[$key] = @mb_convert_encoding($value, 'UTF-8', $this->ApiConfig->Charset);
                    }
                } else {
                    $param = @mb_convert_encoding($param, 'UTF-8', $this->ApiConfig->Charset);
                }
            } elseif (function_exists('iconv')) {
                if (is_array($param)) {
                    foreach ($param as $key => $value) {
                        $param[$key] = @iconv($this->ApiConfig->Charset, 'UTF-8', $value);
                    }
                } else {
                    $param = @iconv($this->ApiConfig->Charset, 'UTF-8', $param);
                }
            }
        }

        return $param;
    }

    private function FormatTaobaoData($data) {
        if (strtoupper($this->ApiConfig->Charset) != 'UTF-8') {
            if (function_exists('mb_convert_encoding')) {
                $data = str_replace('utf-8', $this->ApiConfig->Charset, $data);
                $data = @mb_convert_encoding($data, $this->ApiConfig->Charset, 'UTF-8');
            } elseif (function_exists('iconv')) {
                $data = str_replace('utf-8', $this->ApiConfig->Charset, $data);
                $data = @iconv('UTF-8', $this->ApiConfig->Charset, $data);
            }
        }

        return $data;
    }

    /**
     * @return Taoapi
     */
    public function Send($mode = 'GET', $format = 'xml') {
        $imagesArray = $this->_ArrayModeData = array();

        $tempParam = $this->_userParam;

        foreach ($tempParam as $key => $value) {
            if (is_array($value)) {
                if (!empty($value['image'])) {
                    $imagesArray = $value;
                }
                unset($tempParam[$key]);
            } elseif (trim($value) == '') {
                unset($tempParam[$key]);
            } else {
                $tempParam[$key] = $this->FormatUserParam($value);
            }
        }
        if (!isset($tempParam['api_key'])) {

            $systemdefault['api_key'] = key($this->ApiConfig->AppKey);
            $systemdefault['format'] = strtolower($format);
            $systemdefault['v'] = strpos($this->ApiConfig->Version, '.') ? $this->ApiConfig->Version : $this->ApiConfig->Version . '.0';
            if ($this->ApiConfig->Version == 2) {
                $systemdefault['sign_method'] = strtolower($this->ApiConfig->SignMode);
            }
            $systemdefault['timestamp'] = date('Y-m-d H:i:s');

            $tempParam = array_merge($tempParam, $systemdefault);
            $this->_userParam = array_merge($this->_userParam, $systemdefault);
        }

        $cacheid = $tempParam;

        unset($cacheid['timestamp']);

        $cacheid = md5($this->createStrParam($cacheid));

        $method = !empty($tempParam['method']) ? $tempParam['method'] : '';

        $this->Cache->setMethod($method);

        if (!$this->taobaoData = $this->Cache->getCacheData($cacheid)) {

            $mode = strtoupper($mode);

            $ReadMode = array_key_exists($mode, $this->ApiConfig->PostMode) ? $this->ApiConfig->PostMode[$mode] : $this->ApiConfig->PostMode['GET'];

            if ($ReadMode == 'postImageSend') {
                $this->taobaoData = $this->$ReadMode($tempParam, $imagesArray);
            } else {
                $this->taobaoData = $this->$ReadMode($tempParam);
            }

            $error = $this->getArrayData($this->taobaoData);

            $this->ApiCallLog();

            if (isset($error['code'])) {

                if (in_array($error['code'], array(4, 5, 6, 7, 8, 25))) {
                    $this->_systemParam['apicount'] = empty($this->_systemParam['apicount']) ? 1 : $this->_systemParam['apicount'] + 1;
                    if ($this->_systemParam['apicount'] < count($this->ApiConfig->AppKey)) {
                        next($this->ApiConfig->AppKey);
                        $this->_userParam['api_key'] = key($this->ApiConfig->AppKey);
                        return $this->Send($mode, $format);
                    }
                }

                if ($this->ApiConfig->RestNumberic && empty($this->_systemParam['apicount'])) {

                    $this->ApiConfig->RestNumberic = $this->ApiConfig->RestNumberic - 1;

                    return $this->Send($mode, $format);
                } else {
                    $tempParam['sign'] = $this->_systemParam['sign'];

                    $this->_errorInfo = new Taoapi_Exception($error, $tempParam, $this->ApiConfig->CloseError, $this->ApiConfig->Errorlog);

                    if (!$this->ApiConfig->CloseError) {
                        echo $this->FormatTaobaoData($this->_errorInfo->getErrorInfo());
                    }
                }
            } else {
                $this->Cache->saveCacheData($cacheid, $this->taobaoData);
            }
        }
        return $this;
    }

    public function ApiCallLog() {
        if ($this->ApiConfig->ApiLog) {
            $apilogpath = dirname(__FILE__) . '/api_call_log';
            if (!is_dir($apilogpath)) {
                @mkdir($apilogpath);
            }
            if ($fp = @fopen($apilogpath . '/' . key($this->ApiConfig->AppKey) . '_' . date('Y-m-d') . '.log', 'a')) {
                $logparam = $this->_userParam;
                unset($logparam['fields']);
                @fwrite($fp, implode("\t", $logparam) . "\r\n");
                fclose($fp);
            }
        }
    }

    public function getXmlData() {
        if (empty($this->taobaoData)) {
            return false;
        }
        return $this->FormatTaobaoData($this->taobaoData);
    }

    public function getJsonData() {
        if (empty($this->taobaoData)) {
            return false;
        }
        if (substr($this->taobaoData, 0, 1) != '{') {

            if ($this->_userParam['format'] == 'xml') {
                $Charset = $this->ApiConfig->Charset;
                $this->ApiConfig->Charset = "UTF-8";
                $Data = $this->getArrayData($this->taobaoData);
                $this->ApiConfig->Charset = $Charset;
            }

            $Data = json_encode($Data);
            if (strpos($_SERVER['SERVER_SIGNATURE'], "Win32") > 0) {
                $Data = preg_replace("#\\\u([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])#ie", "iconv('UCS-2','UTF-8',pack('H4', '\\1\\2'))", $Data);
            } else {
                $Data = preg_replace("#\\\u([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])#ie", "iconv('UCS-2','UTF-8',pack('H4', '\\2\\1'))", $Data);
            }
            $Data = $this->FormatTaobaoData($Data);
        } else {
            $Data = $this->taobaoData;
        }
        return $Data;
    }

    public function getArrayData() {
        if (empty($this->taobaoData)) {
            return false;
        }

        if (!empty($this->_ArrayModeData[$this->ApiConfig->Charset])) {
            return $this->_ArrayModeData[$this->ApiConfig->Charset];
        }

        if ($this->_userParam['format'] == 'json') {

            $json = json_decode($this->taobaoData, true);
            return isset($json['rsp']) ? $json['rsp'] : $json;
        } elseif ($this->_userParam['format'] == 'xml') {

            $xmlCode = simplexml_load_string($this->taobaoData, 'SimpleXMLElement', LIBXML_NOCDATA);

            $taobaoData = $this->get_object_vars_final($xmlCode);

            if (strtoupper($this->ApiConfig->Charset) != 'UTF-8') {
                $taobaoData = $this->get_object_vars_final_coding($taobaoData);
            }

            $this->_ArrayModeData[$this->ApiConfig->Charset] = $taobaoData;

            return $taobaoData;
        } else {
            return false;
        }
    }

    /**
     * 返回错误提示信息
     *
     * @return array
     */
    public function getErrorInfo() {
        if ($this->_errorInfo) {
            if (is_object($this->_errorInfo)) {

                return $this->FormatTaobaoData($this->_errorInfo->getErrorInfo());
            } else {
                return $this->FormatTaobaoData($this->_errorInfo);
            }
        }
    }

    /**
     * 返回提交参数
     *
     * @return array
     */
    public function getParam() {
        return $this->_userParam;
    }

    private function JoinSign($paramArr) {
        $sign = '';
        foreach ($paramArr as $key => $val) {
            if (is_array($val)) {
                $sign .= $this->JoinSign($val);
            } elseif ($key != '' && $val !== '') {
                $sign .= $key . $val;
            }
        }

        return $sign;
    }

    public function SignVersion2($paramArr) {
        if (strtolower($this->ApiConfig->SignMode) == 'hmac') {
            ksort($paramArr);
            $sign = $this->JoinSign($paramArr);
            $sign = strtoupper(bin2hex(mhash(MHASH_MD5, $sign, current($this->ApiConfig->AppKey))));
        } else {
            ksort($paramArr);
            $sign = $this->JoinSign($paramArr);
            $sign = strtoupper(md5(current($this->ApiConfig->AppKey) . $sign . current($this->ApiConfig->AppKey)));
        }

        return $sign;
    }

    public function SignVersion1($paramArr) {
        $sign = current($this->ApiConfig->AppKey);
        ksort($paramArr);
        $sign .= $this->JoinSign($paramArr);
        $sign = strtoupper(md5($sign));

        return $sign;
    }

    public function createSign($paramArr) {
        $Version = 'SignVersion' . intval($this->ApiConfig->Version);

        if (method_exists($this, $Version)) {
            $sign = $this->{$Version}($paramArr);
        }

        $this->_systemParam['sign'] = $sign;
        return $sign;
    }

    static public function createStrParam($paramArr) {
        $strParam = array();
        foreach ($paramArr as $key => $val) {
            if ($key != '' && $val !== '') {
                $strParam [] = $key . '=' . urlencode($val);
            }
        }
        return implode('&', $strParam);
    }

    private $_systemParam;

    public function getSend($paramArr) {
        //组织参数
        $this->_systemParam['sign'] = $this->createSign($paramArr);
        $paramArr['sign'] = $this->_systemParam['sign'];
        $strParam = $this->createStrParam($paramArr);
        $this->_systemParam['url'] = $this->ApiConfig->Url . '?' . $strParam;
        //访问服务
        self::$Taoapi_Util->fetch($this->_systemParam['url']);
        $result = self::$Taoapi_Util->results;
        //返回结果
        return $result;
    }

    /**
     * 以POST方式访问api服务
     * @param $paramArr：api参数数组
     * @return $result
     */
    public function postSend($paramArr) {
        //组织参数，Taoapi_Util类在执行submit函数时，它自动会将参数做urlencode编码，所以这里没有像以get方式访问服务那样对参数数组做urlencode编码
        $this->_systemParam['sign'] = $this->createSign($paramArr);
        $paramArr['sign'] = $this->_systemParam['sign'];
        $this->_systemParam['url'] = array($this->ApiConfig->Url, $paramArr);
        //访问服务
        self::$Taoapi_Util->submit($this->ApiConfig->Url, $paramArr);
        $result = self::$Taoapi_Util->results;
        //返回结果
        return $result;
    }

    /**
     * 以POST方式访问api服务，带图片
     * @param $paramArr：api参数数组
     * @param $imageArr：图片的服务器端地址，如array('image' => '/tmp/cs.jpg')形式
     * @return $result
     */
    public function postImageSend($paramArr, $imageArr) {
        //组织参数
        $this->_systemParam['sign'] = $this->createSign($paramArr);
        $paramArr['sign'] = $this->_systemParam['sign'];
        //访问服务
        self::$Taoapi_Util->_submit_type = "multipart/form-data";
        $this->_systemParam['url'] = array($this->ApiConfig->Url, $paramArr, $imageArr);
        self::$Taoapi_Util->submit($this->ApiConfig->Url, $paramArr, $imageArr);
        $result = self::$Taoapi_Util->results;
        //返回结果
        return $result;
    }

    private function get_object_vars_final($obj) {
        if (is_object($obj)) {
            $obj = get_object_vars($obj);
        }

        if (is_array($obj)) {
            foreach ($obj as $key => $value) {
                $obj[$key] = $this->get_object_vars_final($value);
            }
        }
        return $obj;
    }

    private function get_object_vars_final_coding($obj) {
        foreach ($obj as $key => $value) {
            if (is_array($value)) {
                $obj[$key] = $this->get_object_vars_final_coding($value);
            } else {
                $obj[$key] = $this->FormatTaobaoData($value);
            }
        }
        return $obj;
    }

    public function getUrl() {
        return!empty($this->_systemParam['url']) ? $this->_systemParam['url'] : '';
    }

    public function getSign() {
        return!empty($this->_systemParam['sign']) ? $this->_systemParam['sign'] : '';
    }

}